package com.zts.modules.sys.controller;

import com.zts.base.BasicResourceAssembler;
import com.zts.base.exception.ServiceException;
import com.zts.base.exception.SourceNotFoundException;
import com.zts.base.exception.UnauthorizedException;
import com.zts.base.page.PageBound;
import com.zts.base.resource.ListResource;
import com.zts.common.Constant;
import com.zts.modules.sys.entity.Resource;
import com.zts.modules.sys.entity.Role;
import com.zts.modules.sys.entity.User;
import com.zts.modules.sys.entity.UserGroupRelation;
import com.zts.modules.sys.entity.model.UserModel;
import com.zts.modules.sys.entity.resource.ResourceResource;
import com.zts.modules.sys.entity.resource.RoleResource;
import com.zts.modules.sys.entity.resource.UserResource;
import com.zts.modules.sys.service.ResourceService;
import com.zts.modules.sys.service.RoleService;
import com.zts.modules.sys.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by Zhangkh on 2017/6/2.
 */
@RestController("userController")
@RequestMapping("/v1/users")
@Api(value = "users", description = "用户相关API")
@ExposesResourceFor(UserResource.class)
public class UserController {
    private static Logger logger = LoggerFactory.getLogger(UserController.class);
    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @Autowired
    ResourceService resourceService;

    private BasicResourceAssembler<User, UserResource> userAssembler =
            new BasicResourceAssembler<>(UserController.class, UserResource.class);

    private BasicResourceAssembler<Role, RoleResource> roleAssembler =
            new BasicResourceAssembler<>(UserController.class, RoleResource.class);
    private BasicResourceAssembler<Resource, ResourceResource> resourceAssembler =
            new BasicResourceAssembler<>(UserController.class, ResourceResource.class);


    /**
     * 查询全部用户
     * 支持传入的参数参与模糊查询。
     *
     * @param userModel
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUsers", httpMethod = "GET", value = "获取人员分页信息")
    public final ListResource<UserResource> getUsers(@ApiParam(required = false, value = "用户查询条件")
                                                     @ModelAttribute("userModel") UserModel userModel,
                                                     PageBound pageBound) {
        List<User> list = userService.getList(userModel.transEntity(), pageBound);
        return userAssembler.toListResource(list);
    }

    /**
     * 根据用户的关键key，获取唯一的用户。
     * 传入的key 做 or 查询。
     *
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/checkexist", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUserByLoginKey", httpMethod = "GET", value = "根据用户名查询用户")
    public final UserResource getUser(@ApiParam(required = true, value = "登录用户名")
                                      @RequestParam(value = "loginKey", required = true) String loginKey) {
        User user = userService.getByLoginKey(loginKey);
        return userAssembler.toResource(user);
    }

    /**
     * 根据id获取用户信息
     *
     * @param id
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUserById", httpMethod = "GET", value = "根据主键id查询用户")
    public final UserResource getUserById(@ApiParam(required = true, value = "用户主键id")
                                          @PathVariable("id") Long id) {
        User user = userService.get(id);
        return userAssembler.toResource(user);
    }

    /**
     * 新增用户
     *
     * @param userModel
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    @RequestMapping(value = "", method = RequestMethod.POST,
            consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE,
                    MediaType.MULTIPART_FORM_DATA_VALUE,
                    MediaType.APPLICATION_JSON_VALUE},
            produces = MediaType.APPLICATION_JSON_VALUE)
    //方法仅处理request Content-Type为“application/x-www-form-urlencoded”和“multipart/form-data” 类型的请求。
    //方法仅处理request请求中Accept头中包含了"application/json"的请求，同时返回的内容类型为application/json;
    @ApiOperation(notes = "insertUser", httpMethod = "POST", value = "新增用户")
    public final UserResource insertUser(@ApiParam(required = true, value = "用户信息，接收数据格式为json")
                                         @RequestBody UserModel userModel) {
        User user = userService.insert(userModel.transEntity());
        return userAssembler.toResource(user);
    }

    /**
     * 修改用户
     *
     * @param id
     * @param userModel
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    @RequestMapping(value = "/{id}", method = RequestMethod.PUT,
            consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE,
                    MediaType.MULTIPART_FORM_DATA_VALUE,
                    MediaType.APPLICATION_JSON_VALUE},
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "updateUser", httpMethod = "PUT", value = "根据主键id修改用户信息")
    public final int updateUser(@ApiParam(required = true, value = "用户主键id") @PathVariable("id") Long id,
                                @ApiParam(required = true, value = "要修改的用户信息项及对应修改的值，接收数据格式为json")
                                @RequestBody UserModel userModel) {
        userModel.setId(id);
        return userService.update(userModel.transEntity());
    }

    /**
     * 删除用户
     *
     * @param id
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "deleteUser", httpMethod = "DELETE", value = "根据主键id删除用户")
    public final int deleteUser(@ApiParam(required = true, value = "用户主键id") @PathVariable("id") Long id) {
        User user = new User();
        user.setId(id);
        return userService.delete(user);
    }

    /**
     * 用户名和密码校验
     *
     * @param loginKey
     * @param password
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/checkvalid", method = RequestMethod.POST,
            consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "checkValid", httpMethod = "POST", value = "根据关键信息（用户名）校验密码")
    public final UserResource checkValid(@ApiParam(required = true, value = "用户名")
                                         @RequestParam(value = "loginKey") String loginKey,
                                         @ApiParam(required = true, value = "密码")
                                         @RequestParam(value = "password") String password) {

        User user = userService.checkValid(loginKey, password);
        return userAssembler.toResource(user);
    }

    /**
     * 修改用户密码
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    @RequestMapping(value = "/{id}/password", method = RequestMethod.PUT,
            consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE,
                    MediaType.MULTIPART_FORM_DATA_VALUE},
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "updatePasswordById", httpMethod = "PUT", value = "根据主键id修改用户密码")
    public final int updatePasswordById(@ApiParam(required = true, value = "用户标识")
                                        @PathVariable(value = "id") Long id,
                                        @ApiParam(required = true, value = "旧密码")
                                        @RequestParam(value = "oldPassword") String oldPassword,
                                        @ApiParam(required = true, value = "新密码")
                                        @RequestParam(value = "newPassword") String newPassword) {
        return userService.updatePassword(id, oldPassword, newPassword);
    }

    /**
     * 修改用户密码
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    @RequestMapping(value = "/password", method = RequestMethod.PUT,
            consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE,
                    MediaType.MULTIPART_FORM_DATA_VALUE},
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "updatePassword", httpMethod = "PUT", value = "根据用户名修改用户密码")
    public final int updatePassword(@ApiParam(required = true, value = "用户名")
                                    @RequestParam(value = "loginName") String loginName,
                                    @ApiParam(required = true, value = "旧密码")
                                    @RequestParam(value = "oldPassword") String oldPassword,
                                    @ApiParam(required = true, value = "新密码")
                                    @RequestParam(value = "newPassword") String newPassword) {
        return userService.updatePassword(loginName, oldPassword, newPassword);
    }

    //========================================用户与角色相关接口================================================

    /**
     * 查询用户拥有的角色
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{id}/roles", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUserRoles", httpMethod = "GET", value = "查询用户的拥有的角色")
    public final ListResource<RoleResource> getUserRoles(@ApiParam(required = true, value = "用户标识id")
                                                         @PathVariable(value = "id") Long userId) {

        List<Role> roles = userService.getUserRoles(userId);
        return roleAssembler.toListResource(roles);
    }

    /**
     * 增加用户角色
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{id}/roles", method = RequestMethod.POST,
            consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE,
                    MediaType.MULTIPART_FORM_DATA_VALUE},
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUserRoles", httpMethod = "POST", value = "增加用户角色")
    public final int addUserRoles(@ApiParam(value = "用户的主键id", required = true) @PathVariable("id") Long userId,
                                  @ApiParam(value = "角色的主键id", required = true)
                                  @RequestParam("roleIds") List<Long> roleIds) {
        return roleService.batchGrant(userId, Constant.GRANT_OBJECT_USER, roleIds, Constant.GRANT_TYPE_ASSIGN);
    }

    /**
     * 删除用户的角色
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{id}/roles", method = RequestMethod.DELETE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUserRoles", httpMethod = "DELETE", value = "删除用户的角色")
    public final int deleteUserRoles(@ApiParam(value = "用户的主键id", required = true) @PathVariable("id") Long userId,
                                     @ApiParam(value = "角色的主键id", required = true)
                                     @RequestParam("roleIds") List<Long> roleIds) {
        return roleService.batchGrantDelete(userId, Constant.GRANT_OBJECT_USER, roleIds, Constant.GRANT_TYPE_ASSIGN);
    }


    //========================================用户与资源相关接口=============================================

    /**
     * 查询用户拥有的资源
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{id}/resource", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "getUserResource", httpMethod = "GET", value = "查询用户的拥有的资源")
    public final ListResource<ResourceResource> getUserResource(@ApiParam(required = true, value = "用户标识id")
                                                                @PathVariable(value = "id") Long userId,
                                                                @ApiParam(required = true, value = "资源类型,1:菜单，2：功能")
                                                                @RequestParam(value = "type", required = false, defaultValue = "1")
                                                                        String type) {

        List<Role> roles = userService.getUserRoles(userId);
        List<Long> roleIds = new ArrayList<>();
        for (Role r : roles) {
            roleIds.add(r.getId());
        }

        List resourceList = resourceService.getByRoles(roleIds, type, null);
        return resourceAssembler.toListResource(resourceList);
    }

    /**
     * 查询用户拥有的资源
     *
     * @param
     * @param
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/{id}/granted/resource", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(notes = "haveUserResource", httpMethod = "GET", value = "判断用户是否拥有某种资源权限")
    public final ResourceResource haveUserResource(@ApiParam(required = true, value = "用户标识id")
                                                   @PathVariable(value = "id") Long userId,
                                                   @ApiParam(required = true, value = "资源代码")
                                                   @RequestParam(value = "resourceCode") String resourceCode) {

        List<Role> roles = userService.getUserRoles(userId);
        List<Long> roleIds = new ArrayList<>();
        for (Role r : roles) {
            roleIds.add(r.getId());
        }
        List<Resource> resourceList = resourceService.getByRoles(roleIds, null, resourceCode);
        if (resourceList.isEmpty()) {
            throw new UnauthorizedException("用户没有该资源权限");
        }
        return resourceAssembler.toResource(resourceList.get(0));
    }
}
